001 /*
002 * Copyright 2005-2006 Stephen J. McConnell
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
013 * implied.
014 *
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package net.dpml.library.impl;
020
021 import java.io.File;
022 import java.io.FileNotFoundException;
023 import java.net.URI;
024 import java.net.URISyntaxException;
025 import java.util.ArrayList;
026 import java.util.Properties;
027 import java.util.Hashtable;
028
029 import net.dpml.library.info.LibraryDecoder;
030 import net.dpml.library.info.ImportDirective;
031 import net.dpml.library.info.LibraryDirective;
032 import net.dpml.library.info.ModuleDirective;
033 import net.dpml.library.info.ResourceDirective;
034 import net.dpml.library.info.Scope;
035 import net.dpml.library.Library;
036 import net.dpml.library.Module;
037 import net.dpml.library.ModuleNotFoundException;
038 import net.dpml.library.Resource;
039 import net.dpml.library.ResourceNotFoundException;
040
041 import net.dpml.transit.Artifact;
042
043 import net.dpml.util.Logger;
044
045 /**
046 * Utility class used for construction of a module model from an XML source.
047 *
048 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
049 * @version 1.1.0
050 */
051 public final class DefaultLibrary extends DefaultDictionary implements Library
052 {
053 private static final LibraryDecoder LIBRARY_DECODER = new LibraryDecoder();
054
055 private final LibraryDirective m_directive;
056 private final DefaultModule m_module;
057 private final File m_root;
058 private final Logger m_logger;
059 private final Hashtable m_anonymous = new Hashtable();
060
061 private static LibraryDirective buildLibraryDirective( File source ) throws Exception
062 {
063 return LIBRARY_DECODER.build( source );
064 }
065
066 /**
067 * Creation of a new library. The definition of the indexwill
068 * be resolved by search up the file system for a file named index.xml.
069 * @param logger the assigned logging channel
070 * @exception Exception if an error occurs during defintion loading
071 */
072 public DefaultLibrary( Logger logger ) throws Exception
073 {
074 this( logger, resolveLibrarySource() );
075 }
076
077 /**
078 * Creation of a new library.
079 * @param logger the assigned logging channel
080 * @param source the index source defintion
081 * @exception Exception if an error occurs during defintion loading
082 */
083 public DefaultLibrary( Logger logger, File source ) throws Exception
084 {
085 super( null, buildLibraryDirective( source ) );
086
087 if( null == logger )
088 {
089 throw new NullPointerException( "logger" );
090 }
091
092 m_logger = logger;
093 m_directive = (LibraryDirective) super.getAbstractDirective();
094 m_root = source.getParentFile().getCanonicalFile();
095 m_module = new DefaultModule( this, m_directive );
096
097 getLogger().debug( "loaded root module: " + m_root );
098 System.setProperty( "dpml.library.basedir", m_root.toString() );
099
100 // handle expansion of import directives
101
102 ImportDirective[] imports = m_directive.getImportDirectives();
103 ModuleDirective[] importModuleDirectives = new ModuleDirective[ imports.length ];
104 for( int i=0; i<imports.length; i++ )
105 {
106 ImportDirective include = imports[i];
107 ImportDirective.Mode mode = include.getMode();
108 if( ImportDirective.Mode.FILE.equals( mode ) )
109 {
110 throw new UnsupportedOperationException( "file" );
111 }
112 else
113 {
114 String path = include.getValue();
115 URI uri = new URI( path );
116 getLogger().debug( "loading external import: " + uri );
117 ResourceDirective resource = LIBRARY_DECODER.buildResource( uri );
118 if( resource instanceof ModuleDirective )
119 {
120 ModuleDirective moduleDirective = (ModuleDirective) resource;
121 importModuleDirectives[i] = moduleDirective;
122 }
123 else
124 {
125 final String error =
126 "Not yet equipped to import resource of the type ["
127 + resource.getClass().getName()
128 + ".";
129 throw new IllegalArgumentException( error );
130 }
131 }
132 }
133
134 for( int i=0; i<importModuleDirectives.length; i++ )
135 {
136 ModuleDirective importModuleDirective = importModuleDirectives[i];
137 m_module.addResource( importModuleDirective );
138 }
139
140 // create the top-level modules
141
142 ArrayList primaryDirectives = new ArrayList();
143 ResourceDirective[] directives = m_directive.getResourceDirectives();
144 for( int i=0; i<directives.length; i++ )
145 {
146 ResourceDirective directive = directives[i];
147 //if( directive instanceof ModuleDirective )
148 //{
149 //ModuleDirective md = (ModuleDirective) directive;
150 primaryDirectives.add( directive );
151 //}
152 //else
153 //{
154 // final String error =
155 // "No support in place for non-module top-level resources.";
156 // throw new IllegalArgumentException( error );
157 //}
158 }
159 ResourceDirective[] values = (ResourceDirective[]) primaryDirectives.toArray( new ResourceDirective[0] );
160 for( int i=0; i<values.length; i++ )
161 {
162 ResourceDirective d = values[i];
163 m_module.addResource( d );
164 }
165 }
166
167 //----------------------------------------------------------------------------
168 // Library
169 //----------------------------------------------------------------------------
170
171 /**
172 * Utility operation to sort a collection of resources.
173 * @param resources the resources to sort
174 * @return the sorted resource array
175 */
176 public Resource[] sort( Resource[] resources )
177 {
178 DefaultResource[] array = new DefaultResource[ resources.length ];
179 for( int i=0; i<resources.length; i++ )
180 {
181 array[i] = (DefaultResource) resources[i];
182 }
183 return m_module.sortDefaultResources( array, Scope.TEST );
184 }
185
186 /**
187 * Return an array of the top-level modules within the library.
188 * @return module array
189 */
190 public Module[] getModules()
191 {
192 return m_module.getModules();
193 }
194
195 /**
196 * Return a array of all modules in the library.
197 * @return module array
198 */
199 public Module[] getAllModules()
200 {
201 return m_module.getAllModules();
202 }
203
204 /**
205 * Return a named module.
206 * @param ref the fully qualified module name
207 * @return the module
208 * @exception ModuleNotFoundException if the module cannot be found
209 */
210 public Module getModule( String ref ) throws ModuleNotFoundException
211 {
212 return m_module.getModule( ref );
213 }
214
215 /**
216 * Recursively lookup a resource using a fully qualified reference.
217 * @param ref the fully qualified resource name
218 * @return the resource instance
219 * @exception ResourceNotFoundException if the resource cannot be found
220 */
221 public Resource getResource( String ref ) throws ResourceNotFoundException
222 {
223 return m_module.getResource( ref );
224 }
225
226 /**
227 * <p>Select a set of resource matching a supplied a resource selection
228 * constraint. The constraint may contain the wildcards '**' and '*'.
229 * @param criteria the selection criteria
230 * @param sort if true the returned array will be sorted relative to dependencies
231 * otherwise the array will be sorted alphanumerically with respect to the resource
232 * path
233 * @return an array of resources matching the selction criteria
234 */
235 public Resource[] select( String criteria, boolean sort )
236 {
237 return m_module.select( criteria, false, sort );
238 }
239
240 /**
241 * <p>Select a set of resource matching a supplied a resource selection
242 * constraint. The constraint may contain the wildcards '**' and '*'.
243 * @param criteria the selection criteria
244 * @param local if true restrict selection to local projects
245 * @param sort if true the returned array will be sorted relative to dependencies
246 * otherwise the array will be sorted alphanumerically with respect to the resource
247 * path
248 * @return an array of resources matching the selction criteria
249 */
250 public Resource[] select( String criteria, boolean local, boolean sort )
251 {
252 return m_module.select( criteria, local, sort );
253 }
254
255 /**
256 * Select all local projects with a basedir equal to or deeper than the supplied
257 * directory.
258 * @param base the reference basedir
259 * @return an array of projects within or lower than the supplied basedir
260 */
261 public Resource[] select( File base )
262 {
263 return select( base, true );
264 }
265
266 /**
267 * Select all local projects relative to the supplied basedir.
268 * @param base the reference basedir
269 * @param self if true and the basedir resolves to a project then include the project
270 * otherwise the project will be expluded from selection
271 * @return an array of projects relative to the basedir
272 */
273 public Resource[] select( final File base, boolean self )
274 {
275 String root = base.toString();
276 ArrayList list = new ArrayList();
277 DefaultResource[] resources = m_module.selectDefaultResources( true, "**/*" );
278 for( int i=0; i<resources.length; i++ )
279 {
280 DefaultResource resource = resources[i];
281 File basedir = resource.getBaseDir();
282 if( null != basedir )
283 {
284 String path = basedir.toString();
285 if( path.startsWith( root ) )
286 {
287 if( path.equals( root ) )
288 {
289 if( self )
290 {
291 list.add( resource );
292 }
293 }
294 else
295 {
296 list.add( resource );
297 }
298 }
299 }
300 else
301 {
302 final String error =
303 "Local project list returned a resource with a null basedir ["
304 + resource.getResourcePath()
305 + "].";
306 throw new IllegalStateException( error );
307 }
308 }
309 DefaultResource[] selection = (DefaultResource[]) list.toArray( new DefaultResource[0] );
310 return m_module.sortDefaultResources( selection );
311 }
312
313 /**
314 * Locate a resource relative to a base directory.
315 * @param base the base directory
316 * @return a resource with a matching basedir
317 * @exception ResourceNotFoundException if resource match relative to the supplied base
318 */
319 public Resource locate( File base ) throws ResourceNotFoundException
320 {
321 return m_module.locate( base );
322 }
323
324 //----------------------------------------------------------------------------
325 // Dictionary
326 //----------------------------------------------------------------------------
327
328 /**
329 * Return the property names associated with the dictionary.
330 * @return the array of property names
331 */
332 public String[] getPropertyNames()
333 {
334 return m_module.getPropertyNames();
335 }
336
337 /**
338 * Return a property value.
339 * @param key the property key
340 * @return the property value
341 */
342 public String getProperty( String key )
343 {
344 return m_module.getProperty( key );
345 }
346
347 /**
348 * Return a property value.
349 * @param key the property key
350 * @param value the default value
351 * @return the property value
352 */
353 public String getProperty( String key, String value )
354 {
355 return m_module.getProperty( key, value );
356 }
357 //----------------------------------------------------------------------------
358 // internals
359 //----------------------------------------------------------------------------
360
361 /**
362 * Construct a new locally referenced anonymouse resource.
363 * @param include the dependency include
364 * @return the resource
365 * @exception IllegalArgumentException if the include mode is not URN mode
366 * @exception URISyntaxException if the include urn is invaid
367 */
368 DefaultResource getAnonymousResource( String urn, Properties properties )
369 throws URISyntaxException
370 {
371 if( m_anonymous.containsKey( urn ) )
372 {
373 return (DefaultResource) m_anonymous.get( urn );
374 }
375
376 Artifact artifact = Artifact.createArtifact( urn );
377 String scheme = artifact.getScheme();
378 String group = artifact.getGroup();
379 String name = artifact.getName();
380 String version = artifact.getVersion();
381 String type = artifact.getType();
382
383 ResourceDirective resourceDirective =
384 ResourceDirective.createAnonymousResource( scheme, name, version, type, properties );
385
386 ModuleDirective enclosing = null;
387 String[] elements = group.split( "/", -1 );
388 for( int i = ( elements.length-1 ); i>-1; i-- )
389 {
390 String elem = elements[i];
391 if( i == ( elements.length-1 ) )
392 {
393 enclosing = new ModuleDirective( elem, version, resourceDirective );
394 }
395 else
396 {
397 enclosing = new ModuleDirective( elem, version, enclosing );
398 }
399 }
400 try
401 {
402 DefaultModule root = new DefaultModule( this, m_directive );
403 root.addResource( enclosing );
404 DefaultResource resource = root.getDefaultResource( group + "/" + name );
405 m_anonymous.put( urn, resource );
406 return resource;
407 }
408 catch( Exception e )
409 {
410 final String error =
411 "Internal error while creating an ANONYMOUS resource: "
412 + urn
413 + "].";
414 throw new RuntimeException( error, e );
415 }
416 }
417
418 File getRootDirectory()
419 {
420 return m_root;
421 }
422
423 /**
424 * Return the array of top-level modules.
425 * @return the top-level module array
426 */
427 DefaultModule[] getDefaultModules()
428 {
429 return m_module.getDefaultModules();
430 }
431
432 /**
433 * Recursively lookup a resource using a fully qualified reference.
434 * @param ref the fully qualified resource name
435 * @return the resource instance
436 */
437 DefaultResource getDefaultResource( String ref )
438 {
439 return m_module.getDefaultResource( ref );
440 }
441
442 /**
443 * Return a named module.
444 * @param ref the fully qualified resource name
445 * @return the module
446 */
447 DefaultModule getDefaultModule( String ref )
448 {
449 return m_module.getDefaultModule( ref );
450 }
451
452 //----------------------------------------------------------------------------
453 // selection
454 //----------------------------------------------------------------------------
455
456 DefaultResource[] selectDefaultResources( String spec )
457 {
458 return m_module.selectDefaultResources( spec );
459 }
460
461 //----------------------------------------------------------------------------
462 // other internals
463 //----------------------------------------------------------------------------
464
465 private Logger getLogger()
466 {
467 return m_logger;
468 }
469
470 //----------------------------------------------------------------------------
471 // static utilities
472 //----------------------------------------------------------------------------
473
474 private static File resolveLibrarySource() throws FileNotFoundException
475 {
476 String path = System.getProperty( "user.dir" );
477 File dir = new File( path );
478 return resolveLibrarySource( dir );
479 }
480
481 private static File resolveLibrarySource( File dir ) throws FileNotFoundException
482 {
483 if( dir.isFile() )
484 {
485 throw new IllegalArgumentException( "not-a-directory" );
486 }
487 else
488 {
489 File file = new File( dir, INDEX_FILENAME );
490 if( file.isFile() && file.exists() )
491 {
492 return file;
493 }
494 else
495 {
496 File parent = dir.getParentFile();
497 if( parent != null )
498 {
499 return resolveLibrarySource( parent );
500 }
501 }
502 }
503 throw new FileNotFoundException( "index.xml" );
504 }
505 }